package com.alex.controller;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.json.JSONUtil;
import com.alex.entityVo.AggsKeyValue;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.sun.org.apache.xpath.internal.Arg;

import java.io.IOException;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public class SelectJournalTitle {


    public static Map<Integer, Integer> select(String title, String keywords, String abstracts) {

        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(format.format(new Date()) + " 程序开始了~~~");
        System.out.println("用户输入的标题是" + title);
        System.out.println("用户输入的关键词是" + keywords);
        System.out.println("用户输入的摘要是" + abstracts);
        Map<Integer, Integer> list = getJournal(title, keywords, abstracts, InitController.keyWordJidSumMap);
        System.out.println(format.format(new Date()) + " 程序结束了~~");
        return list;

    }

    // 挑选期刊，参数，title、keyWords、abstracts这三个是用户输入的标题、关键词列表、摘要的信息，三项至少输入一个。其中，要求多个关键词时，以"; "进行隔开。keyWordJidSumMap：各关键词在各期刊id号中的数量，这个在重启项目时就应加载出来。
    public static Map<Integer, Integer> getJournal(String title, String keyWords, String abstracts, Map<String, Map<Integer, Integer>> keyWordJidSumMap) {
        Map<String, Integer> keyWordAndSumMap = new ConcurrentHashMap<>();
        Map<Integer, Integer> jidAndSumMap = new ConcurrentHashMap<>();
        Set<String> prepositionsSet = getPrepositionsSet();
        getCodeAndWordSet(title.toLowerCase().trim(), keyWordJidSumMap, jidAndSumMap, keyWordAndSumMap, prepositionsSet);
        getCodeAndWordSet(abstracts.toLowerCase().trim(), keyWordJidSumMap, jidAndSumMap, keyWordAndSumMap, prepositionsSet);
        // 各关键词在存表时，均作了统一的文字处理，并转成了小写，所以，在永驻内存时，也是小写，在匹配用户的输入时，也应是作文字统一处理，并且也应是小写。
        String[] split = keyWords.replace("；", ";").split("; ");
        System.out.println("split的长度是" + split.length);
        for (String keyWord : split) {
            putJidAndSumMap(stringProcessing(keyWord).toLowerCase().trim(), keyWordJidSumMap, keyWordAndSumMap, jidAndSumMap, prepositionsSet);
        }

        Map<String, Integer> mapSortValueIntLen = mapSortValueIntLen(keyWordAndSumMap, 10);
        for (Map.Entry<String, Integer> entry : mapSortValueIntLen.entrySet()) {
            System.out.println("出现前10次的关键词：" + stringRestoration(entry.getKey()) + " 出现的次数：" + entry.getValue());
        }
        List<AggsKeyValue> list = new ArrayList<>();

        Map<Integer, Integer> jidSumMap = mapSortValueLen(jidAndSumMap, 1000);

        for (Map.Entry<Integer, Integer> entry : jidSumMap.entrySet()) {
            System.out.println("期刊jid号：" + entry.getKey() + " 关键词出现的次数：" + entry.getValue());
            list.add(new AggsKeyValue(entry.getKey(), entry.getValue()));
        }
        return jidSumMap;

    }

    // 参数sentence：标题、关键词或摘要中的句子；wordAndCodeSetMap：关键词和数字代号的集合；codeAndWordSetMap：数字代号和关键词的集合。功能：两头卡的方案提取关键词，此方法以空格进行切割再从前和从后进行卡，当无空格时的独立词也可以匹配的上。注意事项：匹配前，需要对sentence进行一次通用的字符串处理，因为加入时都作了。
    public static void getCodeAndWordSet(String str, Map<String, Map<Integer, Integer>> keyWordJidSumMap, Map<Integer, Integer> jidAndSumMap, Map<String, Integer> keyWordAndSumMap, Set<String> prepositionsSet) {
        if (null != str && !str.isEmpty()) {
            String[] paragraphSplit = str.split("\\. ");
            for (String paragraph : paragraphSplit) {
                paragraph = stringProcessing(paragraph);
                if (paragraph.length() > 2) {
                    String[] sentenceSplit = paragraph.split(" ");
                    int len = 0;
                    for (String sentence : sentenceSplit) {
                        String sub = paragraph.substring(len);
                        String[] split1 = sub.split(" ");
                        int len1 = sub.length();
                        for (int j = split1.length - 1; j >= 0; j--) {
                            String keyWord = sub.substring(0, len1);
                            putJidAndSumMap(keyWord, keyWordJidSumMap, keyWordAndSumMap, jidAndSumMap, prepositionsSet);
                            len1 = len1 - split1[j].length() - 1;
                        }
                        len += 1 + sentence.length();
                    }
                }
            }
        }
    }

    private static void putJidAndSumMap(String keyWord, Map<String, Map<Integer, Integer>> keyWordJidSumMap, Map<String, Integer> keyWordAndSumMap, Map<Integer, Integer> jidAndSumMap, Set<String> prepositionsSet) {
        if (keyWord.length() > 2 && keyWordJidSumMap.containsKey(keyWord.toLowerCase())) {
            keyWordAndSumMap.compute(keyWord, (key, value) -> value != null ? (value + 1) : 1);
            Map<Integer, Integer> jidSumMap = keyWordJidSumMap.get(keyWord);
            for (Map.Entry<Integer, Integer> entry : jidSumMap.entrySet()) {
                jidAndSumMap.compute(entry.getKey(), (key, value) -> value != null ? (value + entry.getValue()) : entry.getValue());
            }
        }
    }

    // 对Map的值进行排序，此排序为最大的在前面，输入一个map，再返回一个排序后的新map，String, Integer类型，本排序增加了过滤功能，可以设置要前多少（len）个。
    public static Map<String, Integer> mapSortValueIntLen(Map<String, Integer> mapName, Integer len) {
        Map<String, Integer> result = new LinkedHashMap<>();
        mapName.entrySet().stream()
                .sorted(Map.Entry.<String, Integer>comparingByValue()
                        .reversed()).limit(len).forEachOrdered(e -> result.put(e.getKey(), e.getValue()));
        return result;
    }

    // 参数：jid：期刊的id号；keyWord：关键词，最长32位，包含空格（若不包含空格需要长度大于5位），且长度小于33位，全部小写，去除前后空格及多余空格；tiOrKwOrAbWeight：不同字段的权重，标题为2，关键词为2，摘要为1；KeyWordJidTiKwAbSumMap：每个关键词在每个期刊id号中，标题、关键词、摘要出现的总次数。
    public static void putStrIntInt(int jid, String keyWord, Map<String, Map<Integer, Integer>> keyWordJidTiKwAbSumMap, int tiOrKwOrAbWeight) {
        if (null != keyWord && keyWord.length() > 2 && keyWord.length() < 33) {
            keyWordJidTiKwAbSumMap.compute(keyWord, (k1, map) -> {
                if (map == null) {
                    map = new ConcurrentHashMap<>();
                }
                map.compute(jid, (key, value) -> value != null ? (value + tiOrKwOrAbWeight) : tiOrKwOrAbWeight);
                return map;
            });
        }
    }

    public static void readTable(String tableName,
                                 int onePageNum,
                                 String limit,
                                 String autoId,
                                 String field,
                                 boolean print,
                                 int allTotal,
                                 Map<String, Map<Integer, Integer>> keyWordJidTiKwAbSumMap,
                                 int nowYear,
                                 String keyWordJidSumTable,
                                 String type,
                                 Map<String, Map<Integer, Integer>> keyWordJidSumMap,
                                 Map<Integer, String> jidAndJournalMap,
                                 Set<String> prepositionsSet
    ) throws SQLException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + tableName);
        if (tableNum.get(0).getInt("count") > 0) {
            int tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + tableName).intValue();
            System.out.println("表最大数是" + tableMax);
            Collection<Integer> tableList = new ArrayList<>();
            NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);
            AtomicInteger sumNum = new AtomicInteger(0);
            int showNum = Math.max((tableList.size() / allTotal), 1) + (tableList.size() % allTotal);
            AtomicInteger startNum = new AtomicInteger(1);
            tableList.parallelStream().forEach(startId -> {
                try {
                    List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + tableName + " where " + autoId + " between " + startId + " and " + (startId + onePageNum - 1) + limit);
                    if (print && sumNum.getAndIncrement() % showNum == 0) {
                        System.out.println("\033[31;1m" + format.format(new Date()) + " 读表现在开始：" + startNum.getAndIncrement() + " 总次数：" + allTotal + " 表名：" + tableName + "\033[0m");
                    }
                    for (Entity entry : tableData) {
                        if ("read".equals(type)) {
                            Integer jid = entry.getInt("jid");
                            Integer year = entry.getInt("year");
                            // 如果nowYear是2023，则大于2021而小于2024，即只要2022年和2023年两年的数据。2024年6月则，仍为2023，2024年7月则为2024年，则取2023的2024的了。
                            if (null != jid && jid > 0 && null != year && year > nowYear - 3 && year < nowYear + 1) {
                                // tiOrKwOrAbWeight：不同字段的权重，标题为3，关键词为5，摘要为1
                                putKeyWordJidTiKwAbSumMap(entry.getStr("ti_key"), jid, keyWordJidTiKwAbSumMap, 3);
                                putKeyWordJidTiKwAbSumMap(entry.getStr("kw_key"), jid, keyWordJidTiKwAbSumMap, 5);
                                putKeyWordJidTiKwAbSumMap(entry.getStr("ab_key"), jid, keyWordJidTiKwAbSumMap, 1);
                            }
                        }
                        if ("jour".equals(type)) {
                            Integer jid = entry.getInt(autoId);
                            String name = entry.getStr("name");
                            if (null != jid && jid > 0 && null != name && name.length() > 3) {
                                jidAndJournalMap.put(jid, name);
                            }
                        }
                        if ("memory".equals(type)) {

                            String keyWord = entry.getStr("key_word");
                            try {
                                List<String> list = JSONObject.parseArray(keyWord, String.class);
                                String s = list.get(0);
                                String mapStr = entry.getStr("map");
                                if (null != mapStr && mapStr.length() > 5 && null != keyWord && keyWord.length() > 5) {
                                    Map<Integer, Integer> map = JSONObject.parseObject(mapStr, new TypeReference<Map<Integer, Integer>>() {
                                    });
                                    keyWordJidSumMap.put(s.trim(), map);
                                }
                            } catch (Exception e) {

                            }

                        }
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            });
        }
        if ("read".equals(type)) {
            keyWordJidTiKwAbSumMap.keySet().parallelStream().forEach(keyWord -> {
                Map<Integer, Integer> jidTiKwAbSumMap = keyWordJidTiKwAbSumMap.get(keyWord);
                // 各关键词在存表时，均作了统一的文字处理，并转成了小写，所以，在永驻内存时，也是小写，在匹配用户的输入时，也应是作文字统一处理，并且也应是小写。
                String generic = stringProcessing(keyWord).toLowerCase();
                int sum = 0;
                for (Map.Entry<Integer, Integer> entry : jidTiKwAbSumMap.entrySet()) {
                    sum += entry.getValue();
                }
                try {
                    Db.use().insert(Entity.create(keyWordJidSumTable).set("key_word", generic)
                            .set("sum", sum)
                            .set("map", JSONUtil.toJsonStr(jidTiKwAbSumMap)));
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            });
        }
    }

    // 对Map的值进行排序，此排序为最大的在前面，输入一个map，再返回一个排序后的新map，Integer, Integer类型，本排序增加了过滤功能，可以设置要前多少（len）个。
    public static Map<Integer, Integer> mapSortValueLen(Map<Integer, Integer> mapName, Integer len) {
        Map<Integer, Integer> result = new LinkedHashMap<>();
        mapName.entrySet().stream()
                .sorted(Map.Entry.<Integer, Integer>comparingByValue()
                        .reversed()).limit(len).forEachOrdered(e -> result.put(e.getKey(), e.getValue()));
        return result;
    }

    // 获取现在年，方法是当前时间戳减去8个月后再取年，即2021年7月1日是2021年，2021年6月1日是2020年。此用于新词发现，最长的比较就是一年半与之前的所有相比。
    public static int getNowYear() {
        long time = System.currentTimeMillis() - 60 * 60 * 24 * 30 * 6 * 1000L;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
        String format = sdf.format(new Date(time));
        if (NumberUtil.isInteger(format)) {
            return Integer.parseInt(format);
        }
        return 1900;
    }

    public static void putKeyWordJidTiKwAbSumMap(String str, int jid, Map<String, Map<Integer, Integer>> keyWordJidTiKwAbSumMap, int tiOrKwOrAbWeight) {
        if (null != str && str.length() > 5) {
            try {
                List<String> list = JSONObject.parseArray(str, String.class);
                for (String keyWord : list) {
                    putStrIntInt(jid, stringProcessing(keyWord).toLowerCase(), keyWordJidTiKwAbSumMap, tiOrKwOrAbWeight);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    // 字符串通用处理。
    public static String stringProcessing(String str) {
        if (null == str) {
            return "";
        }
        // 把中文的一些常见符号，转为英文
        str = str.replace("，", ",").replace("。", ".").replace("？", "?").replace("！", "!").replace("；", ";").replace("“", " \"")
                .replace("”", "\" ").replace("（", "(").replace("）", ")").replace("：", ":").replace("İ", "I")
                // 括号、方括号、引号、句号、问号、感叹号、冒号、逗号、分号、尖括号、斜扛、反斜扛，与词组应分开，前后应加上空格。
                .replace("(", " ( ").replace(")", " ) ").replace("[", " [ ").replace("]", " ] ")
                .replace("<", " < ").replace(">", " > ").replace("/", " / ").replace("\\", " \\ ")
                .replace("\"", " \" ").replace(".", " . ").replace("?", " ? ").replace("!", " ! ")
                .replace(":", " : ").replace(",", " , ").replace(";", " ; ").replaceAll(" +", " ")
                // 单引号、横扛，前后的空格应删除
                .replace(" '", "'").replace("' ", "'").replace(" -", "-").replace("- ", "-").replaceAll(" +", " ").trim();
        // 结尾如果是句号，给予删除。
        str = str.endsWith(".") ? str.substring(0, str.length() - ".".length()).trim() : str;
        str = str.endsWith(":") ? str.substring(0, str.length() - ":".length()).trim() : str;
        str = str.endsWith(",") ? str.substring(0, str.length() - ",".length()).trim() : str;
        str = str.endsWith(";") ? str.substring(0, str.length() - ";".length()).trim() : str;
        return str;
    }

    // 字符串还原。
    public static String stringRestoration(String str) {
        if (null == str) {
            return "";
        }
        // 把中文的一些常见符号，转为英文
        str = str.replaceAll(" +", " ")
                // 括号、方括号、尖括号，前后的空格保留，内部的空格给予删除。
                .replace("( ", "(").replace("(", " (").replace(")", ") ").replace(" )", ")")
                .replace("[ ", "[").replace("[", " [").replace("]", "] ").replace(" ]", "]")
                .replace("< ", "<").replace("<", " <").replace(">", "> ").replace(" >", ">")
                // 斜扛、反斜扛，前后的空格均给予删除。
                .replace(" /", "/").replace("/ ", "/").replace(" \\", "\\").replace("\\ ", "\\")
                // 句号、问号、感叹号、冒号、逗号、分号，前面的空格给予删除。
                .replace(" .", ".").replace(" ?", "?").replace(" !", "!").replace(" :", ":").replace(" ,", ",").replaceAll(" +", " ").trim();
        // 结尾如果是句号，给予删除。
        str = str.endsWith(".") ? str.substring(0, str.length() - ".".length()).trim() : str;
        return str;
    }


    // 获取常见介词的集合
    public static Set<String> getPrepositionsSet() {
        Set<String> set = new HashSet<>();
        set.add("about");
        set.add("above");
        set.add("after");
        set.add("along");
        set.add("among");
        set.add("at");
        set.add("by");
        set.add("down");
        set.add("for");
        set.add("from");
        set.add("in");
        set.add("into");
        set.add("like");
        set.add("near");
        set.add("next");
        set.add("of");
        set.add("off");
        set.add("on");
        set.add("onto");
        set.add("out");
        set.add("over");
        set.add("past");
        set.add("to");
        set.add("under");
        set.add("up");
        set.add("upon");
        set.add("with");
        set.add("the");
        set.add("a");
        set.add("and");
        set.add("et al");
        set.add("it");
        set.add("is");
        set.add("this");
        set.add("if");
        return set;
    }

}